/**
 * @file main.cpp
 * @brief DART-Controller
 * @version 1.0
 * @note 注意extern变量!! 注意变量生命周期(my_init() ERROR)!!  注意数组名不要引用!!  不要随便删除初始化代码!!
 * @attention 发送前要检查发送的MAC地址是否正确!!
 */
#include "main.hpp"

#define RETRY_NUM 2
#define LED_PIN GPIO_NUM_2
#define KEY_PIN GPIO_NUM_9
#define DEFINE_OM_VARIABLES(T, NAME)                \
    om_topic_t *NAME##_p;                           \
    om_com_t NAME##_r;                              \
    om_suber_t *NAME##_s1;                          \
    om_status_t NAME##_s1f(om_msg_t *msg, T *data); \
    T NAME##_t;                                     \
    OM_COM_TYPE(T)                                  \
    NAME##_b;

#define DEFINE_OM_RECV_CB(T, NAME)                                                                                                \
    om_status_t NAME##_s1f(om_msg_t *msg, T *DATA)                                                                                \
    {                                                                                                                             \
        ESP_LOGE(TAG, "[[IN " #NAME "_s1f");                                                                                      \
        T *recvDATA = (T *)(msg->buff);                                                                                           \
        T recvData = *recvDATA;                                                                                                   \
                                                                                                                                  \
        ESP_ERROR_CHECK(esp_event_post_to(dart_events_loop_handle, Dart_Communicate, NAME, &recvData, sizeof(T), portMAX_DELAY)); \
        currentState = ACTIVATED;                                                                                                 \
        ESP_LOGI(TAG, "ACTIVATED");                                                                                               \
        ESP_LOGE(TAG, "IN " #NAME "_s1f]]");                                                                                      \
        return OM_OK;                                                                                                             \
    }

static const char *TAG = "DART-Controller";

DEFINE_OM_VARIABLES(requestPing_t, requestPingData);
DEFINE_OM_VARIABLES(requestOperation_t, requestOperationData);
DEFINE_OM_VARIABLES(responseStatus_t, responseStatusData);
DEFINE_OM_VARIABLES(responseOperation_t, responseOperationData);

bool masterMACEstablished = false;
uint8_t masterMAC[6] = {0x00};
// OM debug log
om_topic_t *myOmLog;
// state config
state_m currentState = IDLE;
// espnow cfg
espnow_config_t espnow_config = {
    .pmk = "ESP_NOW",
    .forward_enable = 1,
    .forward_switch_channel = 0,
    .sec_enable = 0,
    .reserved1 = 0,
    .qsize = 32,
    .send_retry_num = 10,
    .send_max_timeout = 3000,
    .receive_enable = {
        .ack = 1,
        .forward = 1,
        .group = 1,
        .provisoning = 0,
        .control_bind = 0,
        .control_data = 0,
        .ota_status = 0,
        .ota_data = 0,
        .debug_log = 0,
        .debug_command = 0,
        .data = 0,
        .sec_status = 0,
        .sec = 0,
        .sec_data = 0,
        .reserved2 = 0,
    },
};

void dartEventHandler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data);

template <typename T>
void myEspnowSend(om_topic_t *topic, T *data)
{
    ESP_LOGE(TAG, "[[IN SEND");
    esp_err_t ret = ESP_OK;
    ESP_LOG_BUFFER_HEX_LEVEL(TAG, data, sizeof(T), ESP_LOG_DEBUG);
    OM_COM_TYPE(T)
    buffer;
    om_publish(topic, data, sizeof(T), true, false);
    om_com_generate_pack(topic, &buffer);
    size_t size = sizeof(buffer);
    static espnow_frame_head_t frame_head = {
        .broadcast = false,
        .ack = false,
        .retransmit_count = 1,
    };
    if (sizeof(*data) == sizeof(responseStatus_t))
    {
        frame_head = {
            .broadcast = false,
            .ack = false,
            .retransmit_count = 1,
        };
    }
    else
    {
        frame_head = {
            .broadcast = false,
            .ack = true,
            .retransmit_count = 1,
        };
    }
    for (;;)
    {
        // ESP_ERROR_CONTINUE(size <= 0, "");
        ret = espnow_send(ESPNOW_DATA_TYPE_DATA, masterMAC, &buffer, size, &frame_head, portMAX_DELAY);
        // ESP_ERROR_CONTINUE(ret != ESP_OK, "<%s> espnow_send", esp_err_to_name(ret));
        ESP_LOGI(TAG, "<%s> <%s>: [" MACSTR "], <%s>: [" MACSTR "]\n[%d]", esp_err_to_name(ret), "selfMAC", MAC2STR(data->stdMsg.selfMAC), "destMAC", MAC2STR(masterMAC), size);
        break;
    }
    memset(&buffer, 0, size);
    ESP_LOGE(TAG, "IN SEND]]");
}

esp_err_t myEspnowRecvCB(uint8_t *src_addr, void *data, size_t size, wifi_pkt_rx_ctrl_t *rx_ctrl)
{
    ESP_LOGI(TAG, "[[IN RECV_CB");
    ESP_PARAM_CHECK(src_addr);
    ESP_PARAM_CHECK(data);
    ESP_PARAM_CHECK(size);
    ESP_PARAM_CHECK(rx_ctrl);
    if (!masterMACEstablished)
    {
        memcpy(&masterMAC, src_addr, 6);
        espnow_add_peer(masterMAC, NULL);
    }
    ESP_LOGI(TAG, "[" MACSTR "]", MAC2STR(masterMAC));
    om_com_prase_recv(&requestPingData_r, (uint8_t *)data, size, true, false);
    om_com_prase_recv(&requestOperationData_r, (uint8_t *)data, size, true, false);
    ESP_LOGI(TAG, "IN RECV_CB]]");
    return ESP_OK;
}

DEFINE_OM_RECV_CB(requestPing_t, requestPingData);
DEFINE_OM_RECV_CB(requestOperation_t, requestOperationData);
DEFINE_OM_RECV_CB(responseStatus_t, responseStatusData);
DEFINE_OM_RECV_CB(responseOperation_t, responseOperationData);

extern "C" void app_main(void)
{
    led = new LED(LED_PIN);
    Power power;
    esp_event_loop_args_t loop_args = {
        .queue_size = 10,
        .task_name = "dartEventsLoop",
        .task_priority = 5,
        .task_stack_size = 8192,
    };
    ESP_ERROR_CHECK(esp_event_loop_create(&loop_args, &dart_events_loop_handle));
    ESP_ERROR_CHECK(esp_event_handler_register_with(dart_events_loop_handle, Dart_Events, ESP_EVENT_ANY_ID, dartEventHandler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register_with(dart_events_loop_handle, Dart_Communicate, ESP_EVENT_ANY_ID, dartEventHandler, NULL));
    Firmware DartController;
    ESP_ERROR_CHECK(espnow_init(&espnow_config));
    ESP_ERROR_CHECK(espnow_set_config_for_data_type(ESPNOW_DATA_TYPE_DATA, true, myEspnowRecvCB));

    om_init();

    requestPingData_p = om_config_topic(NULL, "ca", "requestPingData_p", sizeof(requestPing_t));
    requestOperationData_p = om_config_topic(NULL, "ca", "requestOperationData_p", sizeof(requestOperation_t));
    responseStatusData_p = om_config_topic(NULL, "ca", "responseStatusData_p", sizeof(responseStatus_t));
    responseOperationData_p = om_config_topic(NULL, "ca", "responseOperationData_p", sizeof(responseOperation_t));

    om_com_create(&requestPingData_r, 128, 5, 128);
    om_com_create(&requestOperationData_r, 128, 5, 128);
    om_com_create(&responseStatusData_r, 128, 5, 128);
    om_com_create(&responseOperationData_r, 128, 5, 128);

    om_com_add_topic(&requestPingData_r, requestPingData_p);
    om_com_add_topic(&requestOperationData_r, requestOperationData_p);
    om_com_add_topic(&responseStatusData_r, responseStatusData_p);
    om_com_add_topic(&responseOperationData_r, responseOperationData_p);

    requestPingData_s1 = om_config_suber(NULL, "dt", &requestPingData_s1f, &requestPingData_t, requestPingData_p); // !!!! 注意，这里的fun_arg仅指已经确定好的消息，而不是消息队列中的消息 !!!!
    requestOperationData_s1 = om_config_suber(NULL, "dt", &requestOperationData_s1f, &requestOperationData_t, requestOperationData_p);
    // responseStatusData_s1 = om_config_suber(NULL, "dt", &responseStatusData_s1f, &responseStatusData_t, responseStatusData_p);
    // responseOperationData_s1 = om_config_suber(NULL, "dt", &responseOperationData_s1f, &responseOperationData_t, responseOperationData_p);

    ESP_LOGI(TAG, "[" MACSTR "][" MACSTR "]", MAC2STR(requestOperationData_t.stdMsg.destMAC), MAC2STR(requestPingData_t.stdMsg.destMAC));
    for (int i = 0; i < 6; ++i)
    {
        requestPingData_t.stdMsg.destMAC[i] = 0x00;
        requestOperationData_t.stdMsg.destMAC[i] = 0x00;
        responseStatusData_t.stdMsg.destMAC[i] = 0x00;
        responseOperationData_t.stdMsg.destMAC[i] = 0x00;
    }
    esp_read_mac(requestPingData_t.stdMsg.selfMAC, ESP_MAC_WIFI_STA);
    esp_read_mac(requestOperationData_t.stdMsg.selfMAC, ESP_MAC_WIFI_STA);
    esp_read_mac(responseStatusData_t.stdMsg.selfMAC, ESP_MAC_WIFI_STA);
    esp_read_mac(responseOperationData_t.stdMsg.selfMAC, ESP_MAC_WIFI_STA);
    // ESP_LOGI(TAG, "[" MACSTR "][" MACSTR "]", MAC2STR(requestOperationData_t.stdMsg.destMAC), MAC2STR(requestPingData_t.stdMsg.destMAC));
    currentState = IDLE;

    ESP_ERROR_CHECK(esp_event_post_to(dart_events_loop_handle, Dart_Communicate, omReserved, NULL, 0, portMAX_DELAY));
    // ESP_LOGI(TAG, "[" MACSTR "][" MACSTR "]", MAC2STR(requestOperationData_t.stdMsg.destMAC), MAC2STR(requestPingData_t.stdMsg.destMAC));
    while (1)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        switch (currentState)
        {
        case IDLE:
            ESP_LOGI(TAG, "IDLE");
            break;
        case IDLE_ACTIVATED:
            ESP_LOGI(TAG, "IDLE_ACTIVATED");
            break;
        case ACTIVATED:
            ESP_LOGI(TAG, "ACTIVATED");
            break;
            // case RESERVED:
            //     ESP_LOGI(TAG, "RESERVED");
            //     break;
        }
    }
}

void dartEventHandler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    if (event_base == Dart_Communicate)
    {
        switch (currentState)
        {
        case IDLE:
            ESP_LOGE(TAG, "[[IN IDLE");
            ESP_LOGE(TAG, "IN IDLE]]");
            break;
        case IDLE_ACTIVATED:
            break;
        case ACTIVATED:
            ESP_LOGE(TAG, "[[IN ACTIVATED");
            if (event_id == omReserved)
            {
                ESP_LOGE(TAG, "IN omReserved");
            }
            else if (event_id == requestPingData) // 下位机处理
            {
                memcpy(&requestPingData_t, (requestPing_t *)event_data, sizeof(requestPing_t));
                ESP_LOGE(TAG, "IN requestPingData");
                responseStatusData_t.batteryLevel = Power::getBatteryLevel();
                responseStatusData_t.batteryVoltage = Power::getBatteryVoltage();
                responseStatusData_t.boostState = Power::getBoostState();
                responseStatusData_t.chargeState = Power::getChargeState();
                responseStatusData_t.chargeCurrent = Power::getWirelessChargeCurrent();
                responseStatusData_t.chargeVoltage = Power::getWirelessChargeVoltage();
                myEspnowSend(responseStatusData_p, &responseStatusData_t);
            }
            else if (event_id == requestPingData) // 下位机处理
            {
                memcpy(&requestPingData_t, (requestPing_t *)event_data, sizeof(requestPing_t));
                ESP_LOGE(TAG, "IN requestPingData");
                responseStatusData_t.batteryLevel = Power::getBatteryLevel();
                responseStatusData_t.batteryVoltage = Power::getBatteryVoltage();
                responseStatusData_t.boostState = Power::getBoostState();
                responseStatusData_t.chargeState = Power::getChargeState();
                responseStatusData_t.chargeCurrent = Power::getWirelessChargeCurrent();
                responseStatusData_t.chargeVoltage = Power::getWirelessChargeVoltage();
                myEspnowSend(responseStatusData_p, &responseStatusData_t);
            }
            else if (event_id == requestOperationData) // 下位机处理
            {
                memcpy(&requestOperationData_t, (requestOperation_t *)event_data, sizeof(requestOperation_t));
                ESP_LOGE(TAG, "IN requestOperationData");
                ESP_LOG_BUFFER_HEX_LEVEL(TAG, &requestOperationData_t, sizeof(requestOperationData_t), ESP_LOG_DEBUG);
                if (requestOperationData_t.operation == OPERATION_PWR)
                {
                    if (requestOperationData_t.operationParam == PWR_ON)
                    {
                        esp_err_t ret = ESP_OK;
                        ret = Power::setBoostState(true);
                        ESP_LOGI(TAG, "BOOST_STATE: %d", ret);
                        if (ret != ESP_OK)
                        {
                            responseOperationData_t.errorCode = OPERATION_FAIL_RESERVED;
                        }
                        else
                        {
                            responseOperationData_t.errorCode = OPERATION_OK;
                        }
                        myEspnowSend(responseOperationData_p, &responseOperationData_t);
                    }
                    else if (requestOperationData_t.operationParam == PWR_OFF)
                    {
                        esp_err_t ret = ESP_OK;
                        ret = Power::setBoostState(false);
                        if (ret != ESP_OK)
                        {
                            responseOperationData_t.errorCode = OPERATION_OK;
                        }
                        else
                        {
                            responseOperationData_t.errorCode = OPERATION_FAIL_RESERVED;
                        }
                        myEspnowSend(responseOperationData_p, &responseOperationData_t);
                    }
                }
                // OTA
                else if (requestOperationData_t.operation == OPERATION_OTA)
                {
                    // Trigger OTA
                    OTA_BEGIN_EVENT_DATA otaBeginData;
                    esp_event_post_to(dart_events_loop_handle, Dart_Events, OTA_BEGIN_EVENT, &otaBeginData, sizeof(OTA_BEGIN_EVENT_DATA), portMAX_DELAY);
                    // ResponseOperation
                    responseOperationData_t.errorCode = OPERATION_OK;
                    myEspnowSend(responseOperationData_p, &responseOperationData_t);
                }
            }
            else if (event_id == responseStatusData)
            {
                ESP_LOGI(TAG, "IN responseStatusData");
            }
            else if (event_id == responseOperationData)
            {
                ESP_LOGI(TAG, "IN responseOperationData");
            }
            ESP_LOGE(TAG, "IN ACTIVATED]]");
        }
    }
}